/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.simengine.odeevents;

import java.util.Collection;
import org.apache.commons.math.ode.events.EventException;
import org.apache.commons.math.ode.events.LogicalEventHandler;
import simhya.model.store.Function;
import simhya.model.store.Predicate;
import simhya.matheval.SymbolArray;
import simhya.model.transition.AtomicReset;
import simhya.dataprocessing.EventDataCollector;
import simhya.simengine.ProgressMonitor;
import java.util.Collection;
import simhya.simengine.GuardChecker;

/**
 *
 * @author luca
 */
public class InstantaneousEvent implements LogicalEventHandler, PrintableEvent {
    private int id;
    private Function eventCondition;
    private Predicate eventConditionPredicate;
    private Function priorityFunction;
    private SymbolArray vars;
    private AtomicReset [] resets;
    private boolean isStopping;
    private int dimension;
    private boolean useCache;
    private EventDataCollector collector;
    String name = "Instantaneous event";
    private boolean printEvent;
    private ProgressMonitor monitor;
    private Collection<Integer> listOfModifiedTriggers;
    private Collection<Integer> listOfModifiedPriorities;
    private GuardChecker odeFunction;

    public InstantaneousEvent(int id, int dimension, Predicate guard, 
            Function eventCondition, Function priorityFunction, GuardChecker ode) {
        this.id = id;
        this.odeFunction = ode;
        this.eventConditionPredicate = guard;
        this.eventCondition = eventCondition;
        this.priorityFunction = priorityFunction;
        this.dimension = dimension;
        resets = new AtomicReset[0];
        isStopping = false;
        useCache = false;
        vars = SymbolArray.getNewFastEvalSymbolArray(dimension);
        this.collector = null;
        this.printEvent = false;
        this.monitor = null;
        listOfModifiedTriggers = null;
        listOfModifiedPriorities = null;
        
        //System.out.println("A new instantaneous event has been initialized with event condition: " + eventCondition.toModelLanguage());

    }
    
        /**
     * Sets a monitor to print event occurrences to
     * @param monitor a ProgressMonitor or null to deactivate the functionality
     */
    public void setPrintEvent(ProgressMonitor monitor) {
        if (monitor != null) {
            this.monitor = monitor;
            this.printEvent = true;
        } else {
            this.monitor = null;
            this.printEvent = false;
        }
    }

    public void setCollectorInfo(String name, EventDataCollector collector) {
        this.collector = collector;
        this.name = name;
    }

    public void setIsStopping(boolean isStopping) {
        this.isStopping = isStopping;
    }

    public void setReset(AtomicReset[] resets) {
        this.resets = resets != null ? resets : new AtomicReset[0];
    }

    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }

    

    public int eventOccurred(double t, double[] y, boolean increasing) throws EventException {
        if (this.printEvent)
            monitor.signalEvent(name, t);
        //System.out.println("Event " + name + " fired at time " + t);
        if (collector!= null)
            collector.putNextInstantaneousEvent(t, name);
        if (isStopping)
            return STOP;
        else
            return RESET_STATE;
    }

    public double g(double t, double[] y) throws EventException {
//        System.out.println(y[3] + " " + y[5]+ " " + y[8]+ " " + y[9]+ " " + y[10]
//                + " " + y[13]+ " " + y[14]+ " " + y[50]+ " " + y[52]);
        vars.setValuesReference(y);
        double x = useCache ? eventCondition.computeCache(vars) : eventCondition.compute(vars);

//        System.out.println("Computing event " + name + " function at time  " + t + "; value is " + x);
//        System.out.println("condition: " + this.eventCondition.toModelLanguage());
//        System.out.println(y[3] + " " + y[5]+ " " + y[8]+ " " + y[9]+ " " + y[10]
//                + " " + y[13]+ " " + y[14]+ " " + y[50]+ " " + y[52]);
        
        return x;
    }

    public void resetState(double t, double[] y) throws EventException {
        
//        System.out.println("Event " + name + " resetting variables ");
        
        vars.setValuesReference(y);
        for (AtomicReset r : resets)
            if (useCache)
                r.computeNewValuesCache(vars);
            else
                r.computeNewValues(vars);
        for (AtomicReset r : resets)
            r.updateStoreVariables(y);
        //updates ode function 
        odeFunction.updateGuardStatus(id, t, y);
    }

    public double getPriority(double t, double[] y) throws EventException {
        vars.setValuesReference(y);
        double x = useCache ? priorityFunction.computeCache(vars) : priorityFunction.compute(vars);

        //System.out.println("Computing event " + name + " priority at time  " + t + "; value is " + x);

        return x;
    }

    @Override
    public String toString() {
        return "InstantaneousEvent{" + "name=" + name + '}';
    }

    public Integer getId() {
        return this.id;
    }

    public Collection<Integer> getIdsOfEventsWithModifiedPriority() {
        return this.listOfModifiedPriorities;
    }

    public Collection<Integer> getIdsOfEventsWithModifiedTrigger() {
        return this.listOfModifiedTriggers;
    }

    public boolean isEnabled(double t, double[] y) {
        vars.setValuesReference(y);
        return (useCache ? eventConditionPredicate.evaluateCache(vars) : 
                eventConditionPredicate.evaluate(vars)) ||  eventConditionPredicate.changeContinuously();
    }
 
    public void setIdsOfEventsWithModifiedPriority(Collection<Integer> list) {
        this.listOfModifiedPriorities = list;
    }

    public void setIdsOfEventsWithModifiedTrigger(Collection<Integer> list) {
        this.listOfModifiedTriggers = list;
    }
    
}
